Skip to content

onnx: KV cache, AMD-protocol bench mode, and our own ORT build - #683

Merged
bong-water-water-bong merged 3 commits into
mainfrom
1bit/onnx-linux-bench
Oct 31, 2026
Merged

bong-water-water-bong merged 3 commits into
mainfrom
1bit/onnx-linux-bench

Conversation

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator

From the owner's pi session on ryzen (commit 72e67de, 8 files, +774/-49): the ORT runner gets a KV cache (the decode step no longer re-runs the whole sequence), an AMD-protocol benchmark mode that reproduces model_benchmark's stages and units (onnx/BENCHMARK-PARITY.md), onnx/build_ort.sh for our own ORT build, onnx/bench_npu_serve.py, NOTICE and docs. Opened by the orchestrator for review; the squash merge replaces the local merge commits the session made.

🤖 Generated with Claude Code

The exported graph re-ran the whole sequence per step, so `--cached` now traces
the cache as an input/output: one stacked `[2*layers,1,kv_heads,past,head_dim]`
tensor instead of 56 named ones (dynamo cannot place 56 axes against its argument
tree), and no attention_mask/position_ids, which the graph derives from the cache
length -- with past=0 the traced mask path expands a zero-length axis and ONNX
Runtime rejects the shape.

`ort_run --cached` drives it, and `--bench` emits AMD's `model_benchmark` block:
prompt processing, token generation and token sampling as avg/p50/stddev, E2E and
peak RSS, over their prompt-length sweep and their -g/-w/-r defaults, so a row
from this box and a row from theirs mean the same thing.

build_ort.sh pins v1.23.2 and builds it from source (one `#include <cstdint>` for
GCC 15, oneDNN's CMake-4 floor, libdnnl into the prefix or the DNNL EP silently
never registers). BENCHMARK-PARITY.md carries the protocol, the flag mapping and
the measured rows; bench_npu_serve.py measures a server on the same three stages,
because the NPU row comes from our engine and not from someone else's.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: CHANGES (posted as a comment: GitHub refuses --request-changes on a PR owned by the same account; no tprs-ready). One blocker, and it is CI's first gate: tools/copyright.py --check fails on this head. Everything else I was asked to check passes.

Blocker — three files without the notice, so build cannot go green.

$ python3 tools/copyright.py --check
missing copyright notice: onnx/BENCHMARK-PARITY.md
missing copyright notice: onnx/bench_npu_serve.py
missing copyright notice: onnx/build_ort.sh

ci.yml's build job runs exactly this as its first step, and has_notice() (tools/copyright.py:89) wants all three of the copyright line, SPDX-License-Identifier: Apache-2.0, and "Licensed under the Apache License, Version 2.0" inside the first 20 lines. build_ort.sh has the first two but not the third (its header is the two-line form followed by prose); BENCHMARK-PARITY.md needs the <!-- … --> block that onnx/README.md opens with; bench_npu_serve.py needs the same as the other Python files. python3 tools/copyright.py --fix will do all three.

1. The bench reproduces AMD's protocol as BENCHMARK-PARITY.md documents it — checked both sides. The doc quotes their command line (model_benchmark.exe -i <model> -p <model>\prompts.txt -l "2048,1024,512,256,128"), maps every flag with their defaults to ours (-l/-g/-w/-r → --lens/--gen/--warmup/--repeat, same 2048,1024,512,256,128 / 128 / 1 / 5), copies their stage definitions, and states plainly that -t (peak-CPU sampling) is not implemented and that peak working set is VmHWM. The code matches the doc: --bench prints their block in their names and units —

Prompt processing (time to first token):  avg (us) … avg (tokens/s) … p50 (us) … stddev (us)
Token generation:                          avg (us) … avg (tokens/s) … p50 … stddev
Token sampling:                            avg (us) … avg (tokens/s) … p50 … stddev
E2E generation (entire generation loop):
Peak working set size (bytes):             <VmHWM from /proc/self/status>

and it refuses to bench an uncached graph, because "token generation" there would be the prefill wearing another name.

2. No vendor binary is linked or required. build_ort.sh clones https://github.com/microsoft/onnxruntime at v1.23.2 and builds it with --use_xnnpack --use_dnnl (the same three builtin EPs the distro package serves), with one guarded fix — grep -q before, then it asserts the #include <cstdint> is in core/common/semver.h afterwards. There is no providers_ryzenai, no VitisAI wheel, no .dll or prebuilt blob in the diff; those strings appear only in prose explaining why AMD's tool cannot be run here.

3. NOTICE is right. It now covers both paths — the distro libonnxruntime1.23 or the one we build from the published tag with build_ort.sh, naming the single #include <cstdint> source change and that the sources are the published tag (MIT covers it) — and it keeps the standing statement that AMD's VitisAI EP wheel is never linked or redistributed.

4. The numbers carry box, device, dtype and engine — the measured tables are literally those columns (ryzen / CPU 8 threads / fp32 / our ORT 1.23.2 + ort_run --cached; strixhalo / CPU 32 threads / fp32 / same graph, same runner), prefill on its own row set, and the doc explicitly separates definition-parity from speed-parity (theirs is int4 NPU Windows, ours fp32 CPU Linux). The one number that could mislead is labelled: "not comparable to AMD's published one as a speed claim".

Nothing else looked wrong: the cache is one stacked past/present [56,1,8,n,128] tensor carried as a single OrtValue, --bench requires --cached, and the doc names the cost it creates (224 KB/token copy in the graph, 459 MB at n=2048) rather than hiding it.

Fix the three headers and this is READY on the next head — I re-check only the copyright step and the diff, since everything above is already verified.

bong-water-water-bong added 2 commits October 31, 2026 06:50
Decode and prefill are unchanged within noise (16.7->16.7 tok/s at 128,
6.7->6.6 at 2048), so the per-step allocation was not the O(n) cost — the
graph is. Kept because the cache is now exactly two stacks instead of
whatever the arena grew to; the doc says plainly that it is not a speed fix.

Adds the NPU row from our own dx route (decode 72.6 tok/s at 162 tokens,
47.7 at 1335; prefill 63-78, the weak stage), bench_npu.sh to make the
round trip reproducible, and --mode json|stream because the route streams
every token in reasoning_content after the generation completes.
@bong-water-water-bong
bong-water-water-bong merged commit 8b8b7db into main Oct 31, 2026
7 of 8 checks passed
@bong-water-water-bong
bong-water-water-bong deleted the 1bit/onnx-linux-bench branch October 31, 2026 09:57
bong-water-water-bong added a commit that referenced this pull request Oct 31, 2026
…ivate state file (#685)

The paragraph sent public readers to ~/briefs/TORTEP-STATE.md, a lane file that
only exists on our boxes. The numbers are in onnx/BENCHMARK-PARITY.md (#683) and
docs/onnx.md (#647); link those.

Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant